package com.btcode.schedule;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import com.btcode.common.MyLog;
import com.btcode.common.ObjectCloner;
import com.btcode.exception.MsgException;
import com.btcode.log.ILog;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;

/**
 * 管理定时任务
 *
 * @author gislin
 */
public final class ScheduleManager {

    private volatile static ScheduleManager instance;

    public Scheduler sched;

    private ILog log;

    private ScheduleManager(String queartzConfigFile) {
        try {

            SchedulerFactory sf;

            if (queartzConfigFile != null) {
                sf = new StdSchedulerFactory(queartzConfigFile);
            }
            else {
                sf = new StdSchedulerFactory();
            }
            sched = sf.getScheduler();
            log = MyLog.getInstance().getLogger(ScheduleManager.class.getName());
        }
        catch (SchedulerException e) {
            throw new MsgException("初始化quartz失败", e);
        }
    }

    public static ScheduleManager getInstance() {
        if (instance == null) {
            synchronized (ScheduleManager.class) {
                if (instance == null) {
                    instance = new ScheduleManager(null);
                }
            }
        }
        return instance;
    }

    public static ScheduleManager getInstance(String queartzConfigFile) {
        if (instance == null) {
            synchronized (ScheduleManager.class) {
                if (instance == null) {

                    instance = new ScheduleManager(queartzConfigFile);
                }
            }
        }
        return instance;
    }

    public void clearJob() throws SchedulerException {
        try {
            sched.clear();
        }
        catch (Exception e) {

        }
    }

    /**
     * 增加定时任务，但不执行，任务的启动，统一由上层业务决定
     *
     * @param jobClass 要定时执行的任务class，要实现org.quartz.Job接口
     * @param cronSStr 定时规则表达式，0/2 * * * * ?,秒 分 时 天 月 年
     */
    public void addJob(Class<? extends Job> jobClass, String cronSStr) {
        addJob(jobClass, cronSStr, 0, null, null, null);
    }

    public void addJob(Class<? extends Job> jobClass, String cronSStr, String triggerDesc) {
        addJob(jobClass, cronSStr, 0, null, null, triggerDesc);
    }

    /**
     * 增加定时任务，但不执行，任务的启动，统一由上层业务决定
     *
     * @param jobClass 要定时执行的任务class，要实现org.quartz.Job接口
     * @param cronSStr 定时规则表达式，0/2 * * * * ?,秒 分 时 天 月 年
     */
    public void addJob(Class<? extends Job> jobClass, String cronSStr, HashMap<String, Object> data,
        String jobName) {
        addJob(jobClass, cronSStr, data, jobName, null, null);
    }

    /**
     * 增加定时任务，但不执行，任务的启动，统一由上层业务决定
     *
     * @param jobClass 要定时执行的任务class，要实现org.quartz.Job接口
     * @param jobName job Name
     * @param group group Name
     * @param cronSStr 定时规则表达式，0/2 * * * * ?,秒 分 时 天 月 年
     */
    private void addJob(Class<? extends Job> jobClass, String cronSStr,
        Object data, String jobName, String group, String triggerDesc) {

        try {
            if (group == null || group.trim().equals("")) {
                group = "defaultGroup";
            }

            if (jobName == null || jobName.trim().equals("")) {
                jobName = jobClass.getName();
            }

            try {
                if (sched.isStarted()) {
                    throw new MsgException("添加quartz定时任务：" + jobName + "失败,quartz已启动，不能再添加任务");
                }
            }
            catch (SchedulerException e1) {
                throw new MsgException("添加quartz定时任务：" + jobName + "失败", e1);
            }

            JobDetail job = newJob(jobClass).withIdentity(jobName, group).build();
            if (data != null && data instanceof HashMap) {
               job.getJobDataMap().putAll((HashMap)data);
            }

            CronTrigger trigger = newTrigger().withIdentity(jobName, group).withDescription(
                triggerDesc).withSchedule(
                cronSchedule(cronSStr).withMisfireHandlingInstructionIgnoreMisfires()).build();

            sched.deleteJob(job.getKey());
            sched.scheduleJob(job, trigger);

            log.info("添加quartz定时任务：" + jobName + "成功,cronSStr:" + cronSStr);
        }
        catch (SchedulerException e) {
            throw new MsgException("添加quartz定时任务：" + jobName + "失败", e);
        }
    }

    /**
     * 执行指定任务
     * @param jobName
     * @throws SchedulerException
     */
    public void executeJob(String jobName) throws SchedulerException {
        Set<JobKey> jobKeys = sched.getJobKeys(GroupMatcher.anyGroup());

        for(JobKey jobKey : jobKeys){
            if(jobKey.getName().equals(jobName)){
                sched.triggerJob(jobKey);
                return;
            }
        }
    }

    public void restart() {
        try {
            sched.shutdown(false);
            log.info("定时任务关闭成功");
            sched.start();
            log.info("定时任务重启成功");
        }
        catch (SchedulerException e) {
            throw new MsgException("重启quartz定时任务失败", e);
        }
    }

    public void start() {
        try {
            sched.start();
            log.info("定时任务启动成功");
        }
        catch (SchedulerException e) {
            throw new MsgException("启动quartz定时任务失败", e);
        }
    }

    /**
     * 获取当前所有运行的trigger信息
     */
    @SuppressWarnings("unchecked")
    public List<TriggerModel> getTriggers() throws SchedulerException {
        List<TriggerModel> triggerModels = new ArrayList<>();

        Set<JobKey> jobKeys = sched.getJobKeys(GroupMatcher.jobGroupEquals("defaultGroup"));

        System.out.println("getSchedulerName:" + sched.getSchedulerName());

        for (JobKey jobkey : jobKeys) {
            List<Trigger> triggers = (List<Trigger>) sched.getTriggersOfJob(jobkey);
            JobDetail jobDetail = sched.getJobDetail(jobkey);

            /**
             * 由于需要序列化，重新定义一个HashMap装数据
             */
            HashMap<String, Object> dataMap = new HashMap<>();
            JobDataMap temDataMap = jobDetail.getJobDataMap();

            if(temDataMap != null){
                Set<String> keys = temDataMap.keySet();
                for (String key : keys) {
                    /**
                     * driveInstance放的是jobdetail的实例，不能序列化，略过
                     */
                    if (key.equals("driveInstance")) {
                        continue;
                    }
                    dataMap.put(key, temDataMap.get(key));
                }
            }

            try {
                if (temDataMap != null) {
                    dataMap = (HashMap<String, Object>) ObjectCloner.getClone(dataMap);
                }

            }
            catch (ClassNotFoundException | IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            for (Trigger trigger : triggers) {
                TriggerModel model = new TriggerModel();
                model.setState(sched.getTriggerState(trigger.getKey()));
                model.setNextFireTime(trigger.getNextFireTime());
                model.setPreviousFireTime(trigger.getPreviousFireTime());
                model.setName(trigger.getKey().getName());
                model.setDataMap(dataMap);
                triggerModels.add(model);
            }
        }

        return triggerModels;
    }

}
